﻿using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using System.Web;
using System.Linq;
using System.Diagnostics;

namespace Legend.Web.QueryString.Tests.EncryptedQueryStringSerializerTests
{
    public class EncryptedQueryStringSerializerTestCase
    {
        protected EncryptedQueryStringSerializer serializer;

        [SetUp]
        public void SetUp()
        {
            this.OnSetUp();
        }

        protected virtual void OnSetUp()
        {
            this.serializer = new EncryptedQueryStringSerializer("abc", "data");
        }
    }

    [TestFixture]
    public class Constructor
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_key_is_null()
        {
            var s = new EncryptedQueryStringSerializer(null, "key");
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_key_is_empty_string()
        {
            var s = new EncryptedQueryStringSerializer(string.Empty, "key");
        }

        [Test]
        public void should_set_key()
        {
            var s = new EncryptedQueryStringSerializer("abc", "key");
            Assert.AreEqual("abc", s.EncryptionKey);
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_query_string_key_is_null()
        {
            var s = new EncryptedQueryStringSerializer("abc", null);
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_query_string_key_is_empty_string()
        {
            var s = new EncryptedQueryStringSerializer("abc", string.Empty);
        }

        [Test]
        public void should_set_query_string_key()
        {
            var s = new EncryptedQueryStringSerializer("abc", "key");
            Assert.AreEqual("key", s.QueryStringKey);
        }

        [Test]
        public void should_set_query_string_key_to_data_when_overloaded_constructor_is_called()
        {
            var s = new EncryptedQueryStringSerializer("abc");
            Assert.AreEqual("data", s.QueryStringKey);
        }
    }

    [TestFixture]
    public class Serialize
        : EncryptedQueryStringSerializerTestCase
    {
        Dictionary<string, string> values = new Dictionary<string, string>() { 
            { "A", "A" },
        };

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_keyValuePairs_is_null()
        {
            this.serializer.Serialize(null);
        }

        [Test]
        public void should_render_the_same_value_when_called_twice()
        {
            var query1 = this.serializer.Serialize(this.values);
            var query2 = this.serializer.Serialize(this.values);

            Assert.AreEqual(query1, query2);
        }

        [Test]
        public void should_render_different_values_when_different_keys_are_used()
        {
            var query1 = this.serializer.Serialize(this.values);
            this.serializer.EncryptionKey = Guid.NewGuid().ToString();
            var query2 = this.serializer.Serialize(this.values);

            Assert.AreNotEqual(query1, query2);
        }

        [Test]
        public void should_render_a_string_with_correct_start()
        {
            this.serializer.QueryStringKey = "data";
            var query = this.serializer.Serialize(this.values);
            Assert.AreEqual("data=", query.Substring(0, 5));
        }

        [Test]
        public void should_render_encrypted_string()
        {
            this.serializer.EncryptionKey = "key";
            var query = this.serializer.Serialize(this.values);
            
            Assert.AreEqual(
                "data=7i4BjdD4eVMfVHK6BDlDRlaFP8f2ZA3faBqJ6zBM%2fj2eqTMgS2AZsQ9v1OZsfEfkO3C%2fj5OOhV2oHlkzRZ12gdiqbZ1tfPMM1xYV%2beOAPFFSg5glcUnsQl6by1VIJhzGrTzI9T2RKZGg3nDaysOzoh%2b5Lo4tViW8LHC7D2nzld7AscEXnEz%2fgy4zIO5urijvcaTq7a1iO8M6JQRf2NpJ5dBBKLaTzvvmaVvOe8fIhvocTIVeFDAeabInZfeD%2blTE4dUlDjt76BjsybkvNoZ1UGM8elGbieuGSSUoXrRGcvvPHSiniYSnwHUJQ2mywpy%2fL9IKXIQ%2fPfdxNus1GBb14VHQiOh5amUDgcrX7qoVuVY%3d",
                query);
        }
    }

    [TestFixture]
    public class Parse
        : EncryptedQueryStringSerializerTestCase 
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_queryString_is_null()
        {
            this.serializer.Parse(null);
        }

        [Test, ExpectedException(typeof(FormatException))]
        public void should_throw_exception_when_queryString_does_not_have_correct_beginning()
        {
            this.serializer.Parse("lkdsjf");
        }

        [Test, ExpectedException(typeof(FormatException))]
        public void should_throw_exception_when_the_encrypted_value_in_the_string_is_not_correctly_formatted()
        {
            this.serializer.Parse("data=abc");
        }

        [Test]
        public void should_return_pre_determined_keys_and_values()
        {
            this.serializer.EncryptionKey = "key";
            var result = this.serializer.Parse("data=BX2iZ3YMkfOb%2bgxe43mzsLROQJ1BxD2OAHnL2dtGkiPfZbn6R8j%2b5sO7gofjPsmGjGaNLQbKAB%2brsfokoz233YIMxrFtlxiO0OFuEm%2bcoiISP9ddHuiHFH4O%2fkJIiLRqJMVEECw0rf5g0HJCqkSejJYj%2b3aCZJaDrkD4uI42lFn%2fTTzetguhmakQtP2u2AB%2bjJ5rROkGvu0ybLvPD04Lc0gbl8Ay54qqCyYW12OTKjBh%2ftuZw9E%2fIs2DkJ78B2zErLF7ZCRK1pnBCQ7sWV6BglVEjdgU4OyApFR9peW1eHU%2fDhbvXqYRyrKl6BjIgmJN3zCX30qggGDk%2fmc5mYAf8R6UkffJw%2fNaY%2fUAtH9GobA%3d");
            var resultDictionary = result.ToDictionary(k => k.Key, v => v.Value);

            Assert.AreEqual(1, resultDictionary.Count);
            Assert.AreEqual("A", resultDictionary["A"]);
        }
    }

    [TestFixture]
    public class EncryptionKey
        : EncryptedQueryStringSerializerTestCase 
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_Key_is_set_to_null()
        {
            this.serializer.EncryptionKey = null;
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_Key_is_set_to_empty_string()
        {
            this.serializer.EncryptionKey = string.Empty;
        }

        [Test]
        public void should_be_readable_when_set()
        {
            this.serializer.EncryptionKey = "key";
            Assert.AreEqual("key", this.serializer.EncryptionKey);
        }
    }

    [TestFixture]
    public class QueryStringKey
        : EncryptedQueryStringSerializerTestCase
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_Key_is_set_to_null()
        {
            this.serializer.QueryStringKey = null;
        }

        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void should_throw_exception_when_Key_is_set_to_empty_string()
        {
            this.serializer.QueryStringKey = string.Empty;
        }

        [Test]
        public void should_be_readable_when_set()
        {
            this.serializer.QueryStringKey = "key";
            Assert.AreEqual("key", this.serializer.QueryStringKey);
        }
    }
}
